﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
using UnityEngine.Networking;
using LitJson;
using UnityEditor;
using Object = UnityEngine.Object;

namespace EchoFramework
{
    /// <summary>
    /// 资源管理器
    /// </summary>
    public class AssetManager : Singleton<AssetManager>
    {
        /// <summary>
        /// 游戏AB包资源信息Json对象
        /// </summary>
        private AssetRecordConfig assetRecordConfig;

        /// <summary>
        /// 记录AssetBundleRef对象的字典
        /// key:AB包名称
        /// value:对应AssetBundleRef对象
        /// </summary>
        private Dictionary<string, AssetBundleRef> bundleName2BunldeRefDict;

        /// <summary>
        /// 记录AssetRef对象的字典
        /// key:Asset路径
        /// value:对应的AssetRef对象
        /// </summary>
        private Dictionary<string, AssetRef> assetPath2AssetRefDict;

        /// <summary>
        /// 记录已加载的AssetRef对象  用于释放内存
        /// </summary>
        private Queue<AssetRef> recordAssetRefQueue;

        /// <summary>
        /// 资源每次释放所需的间隔(单位：帧)
        /// </summary>
        private int unloadFrameInterval = 10;

        /// <summary>
        /// 下次资源释放的时刻(单位：帧)
        /// </summary>
        private int nextUnloadFrame = -1;

        /// <summary>
        /// 资源管理器初始化
        /// </summary>
        public override void OnAwake()
        {
            base.OnAwake();
            bundleName2BunldeRefDict = new Dictionary<string, AssetBundleRef>();
            assetPath2AssetRefDict = new Dictionary<string, AssetRef>();
            recordAssetRefQueue = new Queue<AssetRef>();
        }

        public override void OnStart(Action action = null)
        {
            base.OnStart(action);
            if (GameEntry.Instance.AssetBundleMode) //AB包模式下加载AssetRecordConfig.json
            {
                StartCoroutine(LoadAssetRecordConfig());
            }
            else
            {
                CallBack();
            }
        }

        public override void OnUpdate()
        {
            base.OnUpdate();
            if (nextUnloadFrame < 0 || TimeManager.FrameSinceGameStart > nextUnloadFrame)
            {
                nextUnloadFrame = TimeManager.FrameSinceGameStart + unloadFrameInterval;
                Unload(); //资源释放
            }
        }

        /// <summary>
        /// 加载Json资源信息
        /// </summary>
        private IEnumerator LoadAssetRecordConfig()
        {
            string path = Path.Combine(Application.streamingAssetsPath, "AssetRecordConfig.json");
            UnityWebRequest request = UnityWebRequest.Get(path);
            yield return request.SendWebRequest();
            if (!string.IsNullOrEmpty(request.error))
            {
                GameLogger.LogError("加载AssetRecordConfig.json出错!!!");
                GameLogger.LogError(request.error);
                yield break;
            }
            
            assetRecordConfig = JsonMapper.ToObject<AssetRecordConfig>(request.downloadHandler.text);
            if (assetRecordConfig == null)
            {
                GameLogger.LogError("AssetRecordConfig.json转为对象失败!!!");
                yield break;
            }
            
            //记录AssetBundleRef字典
            foreach (AssetBundleInfo assetBundleInfo in assetRecordConfig.AssetBundleInfoList)
            {
                AssetBundleRef assetBundleRef = new AssetBundleRef();
                assetBundleRef.AssetBundleInfo = assetBundleInfo;
                bundleName2BunldeRefDict.Add(assetBundleInfo.AssetBundleName, assetBundleRef);
            }
            
            //记录AssetRef字典
            foreach (AssetInfo assetInfo in assetRecordConfig.AssetInfoList)
            {
                AssetRef assetRef = new AssetRef();
                assetRef.AssetInfo = assetInfo;
                assetRef.AssetBundleRef = bundleName2BunldeRefDict[assetInfo.AssetBundleName];
                int count = assetInfo.DependentAssetBundleList.Count;
                if (count > 0)
                {
                    foreach (string assetBundleName in assetInfo.DependentAssetBundleList)
                    {
                        AssetBundleRef assetBundleRef = bundleName2BunldeRefDict[assetBundleName];
                        if (assetRef.DependentAssetBundleRefList == null)
                            assetRef.DependentAssetBundleRefList = new List<AssetBundleRef>();
                        assetRef.DependentAssetBundleRefList.Add(assetBundleRef);
                    }
                }
            
                assetPath2AssetRefDict.Add(assetInfo.AssetPath, assetRef);
            }
            
            CallBack?.Invoke();
        }

        /// <summary>
        /// 加载GameObject
        /// </summary>
        /// <param name="assetPath">资源路径</param>
        /// <param name="parentTrans">需要挂在的父节点</param>
        /// <returns>GameObject对象</returns>
        public T LoadGameObject<T>(string assetPath, Transform parentTrans = null)  where T : MonoBehaviour
        {
            GameObject go = LoadGameObject(assetPath, parentTrans);
            T component = go.GetComponent<T>();
            return component;
        }
        
        /// <summary>
        /// 加载GameObject
        /// </summary>
        /// <param name="assetPath">资源路径</param>
        /// <param name="parentTrans">需要挂在的父节点</param>
        /// <returns>GameObject对象</returns>
        public GameObject LoadGameObject(string assetPath, Transform parentTrans = null)
        {
            AssetRef assetRef = LoadAssetRef<GameObject>(assetPath);
            if (assetPath == null || assetRef.Asset == null)
                return null;
            //实例化资源
            GameObject go = Instantiate(assetRef.Asset) as GameObject;
            if (parentTrans != null && go != null)
            {
                go.transform.SetParent(parentTrans, false);
            }
            if (assetRef.ChildrenList == null)
            {
                assetRef.ChildrenList = new List<GameObject>();
            }

            //把实例化的物体加入到AssetRef的被依赖列表中
            assetRef.ChildrenList.Add(go);
            return go;
        }

        /// <summary>
        /// 加载资源对象
        /// </summary>
        /// <param name="assetPath">资源路径</param>
        /// <param name="bindGo">绑定到的目标物体，用于资源释放检测</param>
        /// <param name="isAutoUnload">是否需要自动释放资源</param>
        /// <typeparam name="T">要加载的资源类型</typeparam>
        /// <returns>T 泛型对象</returns>
        public T LoadAsset<T>(string assetPath, GameObject bindGo, bool isAutoUnload = true) where T : Object
        {
            if (typeof(T) == typeof(GameObject) || (!string.IsNullOrEmpty(assetPath) && assetPath.EndsWith(".prefab")))
            {
                GameLogger.LogError("不可以加载GameObject类型，请直接使用AsestManager.Instance.LoadGameObject接口，path:" + assetPath);
                return null;
            }

            if (bindGo == null)
            {
                GameLogger.LogError("LoadAsset必须传递一个GameObject作为其要绑定的对象！");
                return null;
            }

            AssetRef assetRef = LoadAssetRef<T>(assetPath);
            if (assetRef == null || assetRef.Asset == null)
                return null;
            if (assetRef.ChildrenList == null)
            {
                assetRef.ChildrenList = new List<GameObject>();
            }

            assetRef.ChildrenList.Add(bindGo);
            return assetRef.Asset as T;
        }

        /// <summary>
        /// 加载资源对象
        /// </summary>
        /// <param name="assetPath">资源路径</param>
        /// <param name="isAutoUnload">是否需要自动释放资源</param>
        /// <typeparam name="T">要加载的资源类型</typeparam>
        /// <returns>AssetRef对象</returns>
        private AssetRef LoadAssetRef<T>(string assetPath, bool isAutoUnload = true) where T : Object
        {
            AssetRef assetRef = null;
            if (GameEntry.Instance.AssetBundleMode) //AB包模式
            {
                assetRef = LoadAssetRef_Runtime<T>(assetPath);
            }
            else //非AB包模式
            {
                assetRef = LoadAssetRef_Editor<T>(assetPath);
            }

            if (isAutoUnload && assetRef != null) //需要自动释放的资源
            {
                recordAssetRefQueue.Enqueue(assetRef);
            }

            return assetRef;
        }

        /// <summary>
        /// AB包模式下加载资源对象
        /// </summary>
        /// <param name="assetPath">资源路径</param>
        /// <typeparam name="T">要加载的资源类型</typeparam>
        /// <returns>AssetRef对象</returns>
        private AssetRef LoadAssetRef_Runtime<T>(string assetPath) where T : Object
        {
            AssetRef assetRef = null;
            if (string.IsNullOrEmpty(assetPath))
            {
                return null;
            }

            //找寻对应的AssetRef对象
            assetPath2AssetRefDict.TryGetValue(assetPath, out assetRef);
            if (assetRef == null)
            {
                GameLogger.LogError("未找到资源：" + assetPath);
                return null;
            }

            //如果已加载过资源，直接返回
            if (assetRef.Asset != null)
            {
                return assetRef;
            }

            if (assetRef.DependentAssetBundleRefList != null)
            {
                //处理AssetRef依赖的AssetBundleRef列表
                foreach (AssetBundleRef oneAssetBundleRef in assetRef.DependentAssetBundleRefList)
                {
                    if (oneAssetBundleRef.AssetBundle == null)
                    {
                        string assetBundlePath = Path.Combine(Application.streamingAssetsPath,
                            oneAssetBundleRef.AssetBundleInfo.AssetBundleName);
                        //加载AssetBundle
                        oneAssetBundleRef.AssetBundle = AssetBundle.LoadFromFile(assetBundlePath);
                    }

                    if (oneAssetBundleRef.ChildrenAssetRefList == null)
                    {
                        oneAssetBundleRef.ChildrenAssetRefList = new List<AssetRef>();
                    }

                    oneAssetBundleRef.ChildrenAssetRefList.Add(assetRef);
                }
            }

            //处理AssetRef自身的AssetBundleRef
            AssetBundleRef assetBundleRef = assetRef.AssetBundleRef;
            if (assetBundleRef.AssetBundle == null)
            {
                string assetBundlePath = Path.Combine(Application.streamingAssetsPath,
                    assetBundleRef.AssetBundleInfo.AssetBundleName);
                //加载AssetBundle
                assetBundleRef.AssetBundle = AssetBundle.LoadFromFile(assetBundlePath);
            }

            if (assetBundleRef.ChildrenAssetRefList == null)
            {
                assetBundleRef.ChildrenAssetRefList = new List<AssetRef>();
            }

            assetBundleRef.ChildrenAssetRefList.Add(assetRef);

            //从AssetBundle中提取asset
            assetRef.Asset = assetRef.AssetBundleRef.AssetBundle.LoadAsset<T>(assetPath);
            assetRef.IsGameObject = typeof(T) == typeof(GameObject) && assetRef.AssetInfo.AssetPath.EndsWith(".prefab");

            return assetRef;
        }

        /// <summary>
        /// 编辑器模式下加载资源对象
        /// </summary>
        /// <param name="assetPath">资源路径</param>
        /// <typeparam name="T">要加载的资源类型</typeparam>
        /// <returns>AssetRef对象</returns>
        private AssetRef LoadAssetRef_Editor<T>(string assetPath) where T : Object
        {
            AssetRef assetRef = null;
#if UNITY_EDITOR
            if (string.IsNullOrEmpty(assetPath))
            {
                return null;
            }

            //找寻对应的AssetRef对象
            assetPath2AssetRefDict.TryGetValue(assetPath, out assetRef);
            if (assetRef == null)
            {
                assetRef = new AssetRef();
            }

            //如果已加载过资源，直接返回
            if (assetRef.Asset != null)
            {
                return assetRef;
            }

            assetRef.Asset = AssetDatabase.LoadAssetAtPath<T>(assetPath);
            if (assetRef.Asset == null)
            {
                GameLogger.LogError("未找到资源：" + assetPath);
                return null;
            }

            assetRef.IsGameObject = typeof(T) == typeof(GameObject) && assetPath.EndsWith(".prefab");
#endif
            return assetRef;
        }

        private void Unload()
        {
            if (recordAssetRefQueue.Count <= 0)
                return;

            AssetRef assetRef = recordAssetRefQueue.Dequeue();
            if (assetRef == null || assetRef.ChildrenList == null || assetRef.ChildrenList.Count == 0)
                return;
            for (int i = assetRef.ChildrenList.Count - 1; i >= 0; i--)
            {
                GameObject go = assetRef.ChildrenList[i];
                if (go == null)
                {
                    assetRef.ChildrenList.RemoveAt(i);
                }
            }

            //若当前资源有依赖，则加到队列队尾
            if (assetRef.ChildrenList.Count > 0)
            {
                recordAssetRefQueue.Enqueue(assetRef);
                return;
            }

            //如果当前资源AssetRef没有被任何GameObject所依赖，那么就可以卸载了
            if (assetRef.ChildrenList.Count == 0)
            {
                assetRef.Asset = null;
                Resources.UnloadUnusedAssets();
                if (!GameEntry.Instance.AssetBundleMode)
                    return;
                //对于AssetRef所属的AssetBundle解除关系
                if (assetRef.AssetBundleRef.ChildrenAssetRefList != null)
                {
                    assetRef.AssetBundleRef.ChildrenAssetRefList.Remove(assetRef);
                    if (assetRef.AssetBundleRef.ChildrenAssetRefList.Count == 0)
                    {
                        assetRef.AssetBundleRef.AssetBundle.Unload(true);
                    }
                }

                //对于AssetRef所依赖的那些AssetBundle列表，解除关系
                if (assetRef.DependentAssetBundleRefList != null)
                {
                    foreach (AssetBundleRef bundleRef in assetRef.DependentAssetBundleRefList)
                    {
                        bundleRef.ChildrenAssetRefList.Remove(assetRef);
                        if (bundleRef.ChildrenAssetRefList.Count == 0)
                        {
                            bundleRef.AssetBundle.Unload(true);
                        }
                    }
                }
            }
        }
    }
}